package com.zzmm.socket.qq.client;

import cn.hutool.core.util.ObjectUtil;
import com.zzmm.socket.qq.common.Message;
import com.zzmm.socket.qq.common.QQConstant;
import com.zzmm.socket.qq.common.ConsoleUtil;
import com.zzmm.socket.qq.common.User;
import lombok.SneakyThrows;

import javax.security.sasl.AuthenticationException;
import java.io.*;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

import static com.zzmm.socket.qq.common.QQConstant.MessageType.*;
import static com.zzmm.socket.qq.common.QQConstant.SERVER_PORT;

/**
 * 客户Socket管理
 */
public class ClientSocketManager {

    private final Socket socket;
    private final Scanner scanner;
    private final ObjectInputStream inputStream;
    private final ObjectOutputStream outputStream;
    private final User user;
    private final ExecutorService writeTread;
    private final Queue<Message> messageQueue;

    @SneakyThrows
    public ClientSocketManager(String username, String password) {
        this.socket = new Socket("192.168.7.1", SERVER_PORT);
        // 一定要先创建输出流再创建输入流
        this.inputStream = new ObjectInputStream(socket.getInputStream());
        // ObjectInputStream创建时会读取数据，所以要确保对方有写入数据
        this.outputStream = new ObjectOutputStream(socket.getOutputStream());
        this.writeTread = Executors.newSingleThreadExecutor();
        this.scanner = new Scanner(System.in);

        if ((this.user = connect(username, password)) == null) {
            throw new AuthenticationException("与服务器建立连接失败！");
        }


        this.messageQueue = new LinkedList<>();
        new ReadThread().start();
        new HandlerThread().start();
    }

    /**
     * 连接到服务器
     */
    @SneakyThrows
    private User connect(String username, String password) {
        Message request = Message.builder()
                .content(new User(username, password))
                .messageType(LOGIN)
                .build();
        writeMessage(request);

        // 获取服务器响应
        Message response = (Message) inputStream.readObject();
        // 登录失败
        if (!response.isSuccess()) {
            socket.close();
            return null;
        }
        // 登录成功
        return new User(username, password);
    }

    /**
     * 写消息
     */
    @SneakyThrows
    public void writeMessage(Message message) {
        if (writeTread.isShutdown()) {
            ConsoleUtil.println("writeTread close");
            return;
        }
        if (socket.isClosed()) {
            return;
        }

        writeTread.submit(() -> {
            try {
                outputStream.writeObject(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 获取用户名
     */
    public String getUsername() {
        return user.getUsername();
    }

    /**
     * 释放资源
     */
    @SneakyThrows
    public boolean handlerLogout(Message message) {
        if (!LOGOUT.equals(message.getMessageType())) {
            return false;
        }

        socket.close();
        // 释放线程资源
        writeTread.shutdown();
        return true;
    }

    /**
     * 处理线程
     */
    private final class HandlerThread extends Thread {

        private final Map<Integer, Consumer<Message>> HANDLER = new HashMap<>();

        {
            HANDLER.put(LIST_ONLINE_USER, this::printOnlineUserList);
            HANDLER.put(PRIVATE_CHAT, this::handlerPrivateChat);
            HANDLER.put(GROUP_CHAT, this::handlerGroupChat);
            HANDLER.put(FILE, this::handlerFile);
            HANDLER.put(SERVER_PUSH, this::handlerServerPush);
        }


        private final Consumer<Message> DEFAULT_HANDLER = this::defaultHandler;

        private void defaultHandler(Message message) {
            ConsoleUtil.println("未知的消息：" + message);
        }

        @SneakyThrows
        @Override
        public void run() {

            while (!socket.isClosed()) {
                Thread.sleep(1000); // 本地调试减轻机器压力
                Message message = messageQueue.poll();
                if (ObjectUtil.isNull(message)) {
                    continue;
                }

                HANDLER.getOrDefault(message.getMessageType(), DEFAULT_HANDLER).accept(message);

            }

            ConsoleUtil.println("HandlerThread close");

        }

        /**
         * 处理私聊消息
         */
        private void handlerPrivateChat(Message message) {
            ConsoleUtil.println("==================收到私聊消息==================");
            ConsoleUtil.printf("%s：%s\n", message.getSender(), message.getContent());
        }

        /**
         * 处理群聊消息
         */
        private void handlerGroupChat(Message message) {
            ConsoleUtil.println("==================收到群聊消息==================");
            ConsoleUtil.printf("%s：%s\n", message.getSender(), message.getContent());
        }

        /**
         * 打印在线用户列表
         */
        @SuppressWarnings({"unchecked"})
        private void printOnlineUserList(Message message) {
            List<String> usernames = (List<String>) message.getContent();
            ConsoleUtil.printf("====================%s 在线用户列表====================\n",message.getDateTime());
            for (int i = 0; i < usernames.size(); i++) {
                ConsoleUtil.printf("%d\t%s\n", i + 1, usernames.get(i));
            }
        }

        /**
         * 处理服务器推送
         */
        private void handlerServerPush(Message message) {
            ConsoleUtil.printf("==================%s 收到服务器推送==================\n",message.getDateTime());
            ConsoleUtil.println((String) message.getContent());
        }


        /**
         * 处理文件
         */
        @SneakyThrows
        private void handlerFile(Message message) {
            ConsoleUtil.println("====================收到文件====================");
            String path = ConsoleUtil.getString("收到用户【%s】发来的文件，请输入接收地址：", message.getSender());// 这边有个问题：会和View的scanner冲突

            // 确保内容是 byte[] 类型
            if (!(message.getContent() instanceof byte[])) {
                ConsoleUtil.println("文件内容错误，无法下载！");
                return;
            }

            byte[] data = (byte[]) message.getContent();

            // 使用 try-with-resources 确保 FileOutputStream 被正确关闭
            try (FileOutputStream fileOutputStream = new FileOutputStream(path)) {
                fileOutputStream.write(data);
                fileOutputStream.flush();
                ConsoleUtil.println("文件下载成功！");
            } catch (IOException e) {
                ConsoleUtil.println("文件下载失败：" + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * 读线程
     */
    private final class ReadThread extends Thread {

        @SneakyThrows
        @Override
        public void run() {

            while (!socket.isClosed()) {
                ConsoleUtil.println("客户端正在等待服务器消息...");

                Message message = (Message) inputStream.readObject();

                if (handlerLogout(message)) {
                    break;
                }

                messageQueue.offer(message);
            }

            ConsoleUtil.println("ReadThread close");

        }

    }

}
